/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * kernel.cc																*
 *																			*
 * The entry point to the C++ part of the kernel, handles a lot of runtime	*
 * stuff and gets the kernel ready to start interacting with the user.		*
 *																			*
\***************************************************************************/

// includes
#include <system.h>
#include <iostream>
#include <console.h>
#include <memory.h>
#include <task.h>
#include <disk.h>
#include <gui.h>
#include <keyboard.h>
#include <filesys.h>
#include <multiboot.h>
#include <stdio.h>
#include <paging.h>
#include <stddef.h>
#include <io.h>
#include <cpu.h>
#include <string.h>
#include <reloc_elf.h>
#include <pm.h>	// -- kmcguire 7/10/2007 

// main() must be C so no name mangling occurs
extern "C" int main( multiboot_info_t* mb, unsigned int magic_number );

// global console
Console cons;

/****** GCC EXTERNAL ASSIST FUNCTIONS ******/

// they must be in C
extern "C"
        {
        int __cxa_atexit(void (*f)(void *), void *p, void *d);
        void __cxa_finalize(void *d);
        };

// the DSO handle
void *__dso_handle; /*only the address of this symbol is taken by gcc*/

// one of the global objects
struct object
{
	void (*f)(void*);
	void *p;
	void *d;
} object[32] = {0};

// number of objects
unsigned int iObject = 0;

// sets up the destructors
int __cxa_atexit(void (*f)(void *), void *p, void *d)
{
	if (iObject >= 32) return -1;
	object[iObject].f = f;
	object[iObject].p = p;
	object[iObject].d = d;
	++iObject;
	return 0;
}

// this destroys all objects
void __cxa_finalize(void *d)
{
	unsigned int i = iObject;
	for (; i > 0; --i)
	{
		--iObject;
		object[iObject].f(object[iObject].p);
	}
}

/**** for local static variables ****/

// The ABI requires a 64-bit type
__extension__ typedef int __guard __attribute__((mode (__DI__)));

extern "C" int __cxa_guard_acquire( __guard* g )
{
	return !*(char*)(g);
}

extern "C" void __cxa_guard_release( __guard* g )
{
	*(char*)g = 1;
}

extern "C" void __cxa_guard_abort( __guard* g )
{
}

// pure virtual function failed
extern "C" void __cxa_pure_virtual()
{
	kprintf( "Pure virtual function call (=0) caught!\n" );
}

// new/delete operators

//overload the operator "new"
void* operator new (size_t size)
{
    return kmalloc(size);
}

//overload the operator "new[]"
void* operator new[] (size_t size)
{
    return kmalloc(size);
}

//overload the operator "delete"
void operator delete (void * p)
{
    kfree( p );
}

//overload the operator "delete[]"
void operator delete[] (void * p)
{
    kfree( p );
}

/****** Real Kernel ******/

// STDOUT character output
int stdout_putc( int c )
{
	// print it (this hack is to get it to print safely)
	char tmp[2] = { c, 0 };
	cons.Output( tmp );
}

// STDIN character input
int stdin_getc()
{
	// get from the keyboard buffer
	return getch();
}

// STDOUT 'write' function
int stdout_write( int fd, void* buff, uint_t count )
{
	// create a temporary char* pointer to the void* buffer
	char* tmp = (char*) buff;
	
	// print it all
	tmp[count + 1] = 0;
	cons.Output( tmp );
	
	// return the count
	return count;
}

// STDIN 'read' function
int stdin_read( int fd, void* buff, uint_t count )
{
	// create a temporary char* pointer to the void* buffer
	char* tmp = (char*) buff;
	
	// print it all
	cons.Get( tmp, count );
	
	// return the count
	return count;
}

// idle procedure
void nullproc()
{
	// stdout and stdin device descriptors
	int stdout, stdin;

	// install stdout and stdin (both refer to the same device)
	stdout = stdin = InstallDevice( "console", 0, 0 );
	
	// install the functions (both stdin and stdout are the same)
	INSTFUNCDATA consoleFunctions = { (IOCtlIntFunc) NULL, (IOCtlPtrFunc) NULL, stdin_read, stdout_write, (LSeekFunc) NULL, stdin_getc, stdout_putc };
	InstallFunctions( stdout, consoleFunctions );
	
	// null procedure, called when nothing else to do
	cons.Output( "[NULL] Idle task has started.\n", true );

	// initialize the FAT32 system
	cons.Output( "[FAT ] Initializing FAT32 structures... ", true );
	InitBPB_FAT32( 0 );
	cons.Output( "Done!\n", true );

	// print the welcome
	kprintf( "Welcome to Mattise!\nVersion 1.1, ALPHA\n" );
	// load the shell, and then we're set
	//ELFRelocation elffile;
	kprintf( "[ELF ] Testing ELF relocation...\n" );
	//bool loads = elffile.LoadFile( "dr0/mattise/test.bin" );
	//kprintf( "[ELF ] Loaded the file? %s\n", loads ? "Yes!" : "No!" );
	//bool rel = elffile.RelocateExecutable();
	//kprintf( "[ELF ] Relocated the file? %s\n", rel ? "Yes!" : "No!" );

	// just in case...
#ifdef REALLY_BORED
	char woah[] = "Lorem ipsum dolor sit amet, consectetaur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum Et harumd und lookum like Greek to me, dereud facilis est er expedit distinct. Nam liber te conscient to factor tum poen legum odioque civiuda. Et tam neque pecun modut est neque nonor et imper ned libidig met, consectetur adipiscing elit, sed ut labore et dolore magna aliquam makes one wonder who would ever read this stuff? Bis nostrud exercitation ullam mmodo consequet. Duis aute in voluptate velit esse cillum dolore eu fugiat nulla pariatur. At vver eos et accusam dignissum qui blandit est praesent luptatum delenit aigue excepteur sint occae. Et harumd dereud facilis est er expedit distinct. Nam libe soluta nobis eligent optio est congue nihil impedit doming id Lorem ipsum dolor sit amet, consectetur adipiscing elit, set eiusmod tempor incidunt et labore et dolore magna aliquam. Ut enim ad minim veniam, quis nostrud exerc. Irure dolor in reprehend incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse molestaie cillum. Tia non ob ea soluad incommod quae egen ium improb fugiend. Officia deserunt mollit anim id est laborum Et harumd dereud facilis est er expedit distinct. Nam liber te conscient to factor tum poen legum odioque civiuda et tam. Neque pecun modut est neque nonor et imper ned libidig met, consectetur adipiscing elit, sed ut labore et dolore magna aliquam is nostrud exercitation ullam mmodo consequet. Duis aute in voluptate velit esse cillum dolore eu fugiat nulla pariatur. At vver eos et accusam dignissum qui blandit est praesent. Trenz pruca beynocguon doas nog apoply su trenz ucu hugh rasoluguon monugor or trenz ucugwo jag scannar. Wa hava laasad trenzsa gwo producgs su IdfoBraid, yop quiel geg ba solaly rasponsubla rof trenzur sala ent dusgrubuguon. Offoctivo immoriatoly, hawrgasi pwicos asi sirucor.Thas sirutciun applios tyu thuso itoms ghuso pwicos gosi sirucor in mixent gosi sirucor ic mixent ples cak ontisi sowios uf Zerm hawr rwivos. Unte af phen neige pheings atoot Prexs eis phat eit sakem eit vory gast te Plok peish ba useing phen roxas. Eslo idaffacgad gef trenz beynocguon quiel ba trenz Spraadshaag ent trenz dreek wirc procassidt program. Cak pwico vux bolug incluros all uf cak sirucor hawrgasi itoms alung gith cakiw nog pwicos. Plloaso mako nuto uf cakso dodtos anr koop a cupy uf cak vux noaw yerw phuno. Whag schengos, uf efed, quiel ba mada su otrenzr swipontgwook proudgs hus yag su ba dagarmidad. Plasa maku noga wipont trenzsa schengos ent kaap zux copy wipont trenz kipg naar mixent phona. Cak pwico siructiun ruos nust apoply tyu cak UCU sisulutiun munityuw uw cak UCU-TGU jot scannow. Trens roxas eis ti Plokeing quert loppe eis yop prexs. Piy opher hawers, eit yaggles orn ti sumbloat alohe plok. Su havo loasor cakso tgu pwuructs tyu InfuBwain, ghu gill nug bo suloly sispunsiblo fuw cakiw salo anr ristwibutiun. Hei muk neme eis loppe. Treas em wankeing ont sime ploked peish rof phen sumbloat syug si phat phey gavet peish ta paat ein pheeir sumbloats. Aslu unaffoctor gef cak siructiun gill bo cak spiarshoot anet cak GurGanglo gur pwucossing pwutwam. Ghat dodtos, ig pany, gill bo maro tyu ucakw suftgasi pwuructs hod yot tyubo rotowminor. Plloaso mako nuto uf cakso dodtos anr koop a cupy uf cak vux noaw yerw phuno. Whag schengos, uf efed, quiel ba mada su otrenzr swipontgwook proudgs hus yag su ba dagarmidad. Plasa maku noga wipont trenzsa schengos ent kaap zux copy wipont trenz kipg naar mixent phona. Cak pwico siructiun ruos nust apoply tyu cak UCU sisulutiun munityuw uw cak UCU-TGU jot scannow. Trens roxas eis ti Plokeing quert loppe eis yop prexs. Piy opher hawers, eit yaggles orn ti sumbloat alohe plok. Su havo loasor cakso tgu pwuructs tyu.";
	kprintf( "Stupid placeholder text:\n%s\n", woah );
#endif

	// start the GUI
	//CreateTask( (unsigned int) GUI, 1024 * 1024, "GUI", 10 );
	sched_threadDescription td;
	memset(&td, 0, sizeof(td));
	
	td.entry = (ENTRYADDR)&GUI;
	td.contextId = 0;
	td.processId = 0;
	td.priority = 0;
	td.ustack = pm_alloc() + 4096;
	td.kstack = pm_alloc() + 4096;
	td.cs = 0x08;
	td.ds = 0x10;
	td.eflags = SCHED_EFLAGS32;
	td.medaddr = 0;
	sched_create_thread(&td, 0);	

	// idle
	while( true );
}

// global multiboot info pointer
multiboot_info_t* MultiBootHeader;

// start and end of kernel image
extern unsigned char __end;
extern unsigned char __start;
extern unsigned char __b16code;

// kernel entry point
int main( multiboot_info_t* mb, unsigned int magic_number )
{
	// set the global multiboot header
	MultiBootHeader = mb;
	
	// turn on debugging
	cons.ToggleDebugMode();
	
	// clear the screen
	cons.ClearScreen();

	// -- kmcguire 6/17/2007

	// --- initialize the physical page manager with the GRUB memory map (pm.cc/pm.h)
	pm_init_with_grub_mmap( mb->mmap_addr, mb->mmap_length );
	// --- tell (pm.cc/pm.h) ahead of time where regions of memory are that WE are using! (including the kernel)
	pm_set( ( (uint_t) &__start ), ( ( (uint_t) &__end - (uint_t) &__start ) / 4096 ) + 1, PM_VALID | PM_USED | PM_FLUSH_ONLYVALID );
	// --- keep memory under 1 MB unavailable to normal kernel operations (PM_LEGACY)
	pm_set( 0x0, 0x100, PM_LEGACY | PM_FLUSH_ONLYVALID );
	
	// kill interrupts
	__asm__ __volatile__ ( "cli" );
	
	// install the GDT
	cons.Output( "[GDT ] Installing GDT... ", true );
	gdt_install();
	cons.Output( "Done!\n", true );
	
	// install the IDT
	cons.Output( "[IDT ] Installing IDT... ", true );
	idt_install();
	cons.Output( "Done!\n", true );
	
	// install ISRs
	cons.Output( "[INT ] Installing interrupts (ISR/IRQ)... ", true );
	isrs_install();
	
	// set up IRQ mapping
	irq_install();
	cons.Output( "Done!\n", true );
	
	// initialize the memory manager
	cons.Output( "[MEM ] Installing memory manager... ", true );
	kmalloc( 1 ); /** sets up the heap **/
	cons.Output( "Done!\n", true );
	
	// setup paging
/*	cons.Output( "[MEM ] Setting up paging... ", true );
	InitPaging();
	cons.Output( "Done!\n", true );*/
	
	// do CPUID
	cons.Output( "[CPU0] Executing CPUID... ", true );
	CPUIdent();
	cons.Output( "Done!\n", true );

	// install the timer
	cons.Output( "[TMR ] Installing timer... ", true );
	install_timer();
	cons.Output( "Done!\n", true );
	
	// install the keyboard
	cons.Output( "[KBD ] Installing keyboard... ", true );
	keyboard_install();
	cons.Output( "Done!\n", true );

	// setup the multitasker
	cons.Output( "[MTSK] Initializing multitasking... ", true );
	initmt();
	mtenable();
	cons.Output( "Done!\n", true );

	// can interrupt now
	__asm__ __volatile__ ( "sti" );
	
	// detect hard drives
	cons.Output( "[IDE ] Detecting drives...\n", true );
	DetectDrives();
	cons.Output( "[IDE ] Done!\n", true );

	sched_threadDescription td;
	memset(&td, 0, sizeof(td));
	
	// create system task
	cons.Output( "[MTSK] Creating idle task and enabling tasking... ", true );
	//CreateThread( (unsigned int) nullproc, 1024, "System Idle Process", 1 );
	td.entry = (ENTRYADDR)&nullproc;
	td.contextId = 0;
	td.processId = 0;
	td.priority = 0;
	td.ustack = pm_alloc() + 4096;
	td.kstack = pm_alloc() + 4096;
	td.cs = 0x08;
	td.ds = 0x10;
	td.eflags = SCHED_EFLAGS32;
	td.medaddr = 0;
	sched_create_thread(&td, 0);
	cons.Output( "Done!\n", true );

	// ---- testing the 16bit code ----
	// --kmcguire:
	// 1. Allocate some legacy memory (below 1MB) for code, and copy code there.
	memcpy((void*)pm_allocEx(PM_LEGACY | PM_USED, PM_LEGACY, 0, 1), &__b16code, 20);

	// 2. Create a 16BIT thread and allocate legacy memory for it's stack, while using >1MB memory for the kernel stack.
	td.entry = 0x1000;
	td.contextId = 0;
	td.processId = 0;
	td.priority = 0;
	td.ustack = pm_allocEx(PM_LEGACY | PM_USED, PM_LEGACY, 0, 1) + 4096;
	td.kstack = pm_alloc() + 4096;
	td.cs = 0x00;
	td.ds = 0x10;
	td.eflags = SCHED_EFLAGS16;
	td.medaddr = 0;
	THREADID p = sched_create_thread(&td, 0);

	// loop forever
	while( true );
}
